Retrieving data from a ViewModel in POST method - c#

I have a View model that looks like this
public class ItemViewModel
{
[Required]
public int Id { get; set; }
public string ItemId { get; set; }
public string ItemName { get; set; }
public string MFGNumber { get; set; }
public IList<ItemPartViewModel> Parts { get; set; }
public IList<ItemComponentViewModel> Components{ get; set; }
public IList<ComponentPartViewModel> ComponentParts { get; set; }
public IList<ComponentSubCompViewModel> ComponentSubComps { get; set; }
public IList<SubCompPartViewModel> SubCompParts { get; set; }
public IList<SubCompSubCompViewModel> SubCompSubComps { get; set; }
public IList<SubCompSubCompPartViewModel> SubCompSubCompParts { get; set; }
}
As you can see the Viewmodel also has corresponding view models that look like this
public class ItemPartViewModel
{
[Required]
public int ID { get; set; }
public string PartID { get; set; }
public HtmlString PartLink { get; set; }
public string MFGNumber { get; set; }
public string PartName { get; set; }
public float QtyInItem { get; set; }
public float OnHand { get; set; }
public float OnWorkOrder { get; set; }
public float Committed { get; set; }
public float FSTK { get; set; }
// This is the additional property to contain what user picks
public PartActionType SelectedActionType { get; set; }
}
The ItemViewModel is populated through my OrderSelection GET method that looks like this
public ActionResult SpecialOrderSelection(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
JobOrder jobOrder = db.JobOrders.Find(id);
if (jobOrder == null)
{
return HttpNotFound();
}
ViewBag.JobOrderID = jobOrder.ID;
ItemInstance ii = db.ItemInstances.Where(x => x.serialNumber == jobOrder.serialNumber).FirstOrDefault();
Item item = db.Items.Find(ii.ItemID);
var vm = new ItemViewModel
{
Id = item.ID,
ItemId = item.ItemID,
ItemName = item.Name,
Parts = new List<ItemPartViewModel>(),
Components = new List<ItemComponentViewModel>(),
ComponentParts = new List<ComponentPartViewModel>(),
ComponentSubComps = new List<ComponentSubCompViewModel>(),
SubCompParts = new List<SubCompPartViewModel>(),
SubCompSubComps = new List<SubCompSubCompViewModel>(),
SubCompSubCompParts = new List<SubCompSubCompPartViewModel>()
};
foreach (ItemHasParts ihp in item.IHP)
{
Part part = db.Parts.Find(ihp.PartID);
vm.Parts.Add(new ItemPartViewModel
{
ID = part.ID,
PartID = part.PartID,
PartLink = part.PartIDLink,
MFGNumber = part.MFG_number,
QtyInItem = ihp.qty,
OnHand = part.On_Hand,
OnWorkOrder = part.On_Order_Count(true, true),
Committed = part.CommittedCount(true, true),
FSTK = part.FSTK,
PartName = part.Name,
SelectedActionType = PartActionType.Transfer
});
}
return View(vm);
}
The data then is correctly shown on the selection page. But on this page users must select whether they want to harvest/transfer/or dispose of a part. So once the user has finished selecting their options they hit a 'submit' button. This then POSTS to this method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SpecialOrderSelection(ItemViewModel model)
{
//list of transfers
//list of harvests
//list of disposals
if (ModelState.IsValid)
{
JobOrder jobOrder = db.JobOrders.Find(model.Id);
if (jobOrder == null)
{
return HttpNotFound();
}
ViewBag.JobOrderID = jobOrder.ID;
// do whatever with 'model' and return or redirect to a View
}
//ViewBag.submitted = true;
return RedirectToAction("SpecialOrderSummary", new { ID = jobOrder.ID });
}
The problem here is that for each list, (Parts/Components/ComponentParts/etc.) The ID is null. Why is it null on the POST but not the GET? And how can I fix this so it is not null
Here is the beginning of my View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.Id)
<div class="form-horizontal">
<h2 class="noprint">Special Order Selection</h2>
<p style="color:red" class="noprint">Please select what is to be done with each component/part</p>
<td align="left">
<hr class="noprint" />
<h4 class="noprint"><b>Work Order ID:</b> #Html.DisplayFor(model => j.ID)</h4>
<br class="noprint" />
And here is the payload of it
<form action="/JODetails/SpecialOrderSelection/3092" method="post"><input name="__RequestVerificationToken" type="hidden" value="LETTERSANDNUMBERS" /><input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="Id" name="Id" type="hidden" value="3092" /> <div class="form-horizontal">
<h2 class="noprint">Special Order Selection</h2>
<p style="color:red" class="noprint">Please select what is to be done with each component/part</p>
Here is an example of what a 'Part' in my 'parts' list is returning

Your hidden field in the form is Id, which is the Model.Id (ItemViewModel) and not the part! and yet the property you're inspecting on POST is ID (which is the Part ID). You're looking for the item part ID, but you only have the ItemViewModel Id in the form. Since you have other values for item part properties, you must be iterating over that list of parts somewhere in your form. Add a hidden input for the part ID there.
#{
foreach(var part in Model.Parts) {
#Html.HiddenFor(model => part.ID)
}
}

Related

How to automatically add a name from selected id in View page from controller ASP.NET MVC

I want to get a name from selected id in my view page
First Model
public class Transaction
{
[Key]
public int Id { get; set; }
[Required]
public int supplier_id { get; set; }
[Required]
public string supplier_name { get; set; }
}
Second Model
public class Supplier
{
[Key]
public int supplier_id { get; set; }
[Required]
public string supplier_name { get; set; }
}
View Model
public class EvaluateSheet
{
public IEnumerable<Supplier> Suppliers{ get; set; }
public IEnumerable<Transaction> Transactions { get; set; }
public Transaction Transaction { get; set; }
}
Controller
public IActionResult Sheet11()
{
var sup = _db.Supplier.ToList();
ViewBag.Sup = new SelectList(sup, "supplier_id", "supplier_name");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Sheet11(EvaluateSheet objc)
{
Transaction Model = objc.Transaction;
Transaction transaction = new Transaction();
transaction.Id = Model.Id;
transaction.supplier_id = Model.supplier_id;
transaction.supplier_name = Model.supplier_name;
_db.Transaction.Add(Model);
Model.supplier_name = Model.Supplier.supplier_name.Where(Model.Supplier.supplier_id == Model.supplier_id);
_db.SaveChanges();
return RedirectToAction("Index");
}
View Page
#model EvaluateSheet
<form method="post">
<select asp-for="#Model.Transaction.supplier_id" asp-items="#ViewBag.Sup" value="#ViewBag.Sup" class="form-control">
<option selected disabled>--choose supplier--</option>
</select>
<button type="submit" class="btn btn-primary">Add Transaction</button>
I want to automatically insert supplier_name field after I selected supplier_id in my View page into the database.
I have tried this in my controller but it doesn't work
Model.supplier_name = Model.Supplier.supplier_name.Where(Model.Supplier.supplier_id == Model.supplier_id);
Try this:
Define an integer type variable in EvaluateSheet model for example("public int Sup_id { get; set; }") and tag it with DropDownListFor.
In Controller select specific data from supplier table with the help of new define int Sup_id and copy this data in Model(Transaction).
For example:
Transaction Model = new Transaction();
Supplier supl = null;
supl = _db.Supplier.Where(x => x.supplier_id == objc.Sup_id).FirstOrDefault();
Model.supplier_id = supl.supplier_id;
Model.supplier_name = supl.supplier_name;
_db.Transaction.Add(Model);
_db.SaveChanges();
return RedirectToAction("Index");

Net Core 3.0: multiple checkboxes with integer variables

In my web app I have a self referencing many to many relationship of plants. The M2M relationships are good neighbours and bad neighbours. I want to be able to have a form where a user can check off the both types of neighbours and then save the form.
What I have so far:
For brevity, I will only show code to Good neighbours relation, the bad neighbours is the same.
Models
public class Plant
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<GoodPlants> GoodNeighbours { get; set; }
}
public class GoodPlants
{
public int PlantId { get; set; }
public int GoodNeighbourId { get; set; }
public virtual Plant Plant { get; set; }
public virtual Plant GoodNeighbour {get; set;}
}
My viewmodel EditViewModel:
public class EditViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<PlantIsSelectedViewModel> AllPlants { get; set; }
}
And the model PlantIsSelected
public class PlantIsSelectedViewModel
{
public int id { get; set; }
public string name { get; set; }
public bool isGoodNeighbour { get; set; }
public bool isBadNeighbour { get; set; }
}
I have an Edit method to display the edited plant and all the other plants:
var plant = await _context.Plants.FindAsync(id);
ICollection<Plant> plants = _context.Plants.ToList();
ICollection<GoodPlants> goodNeighbours = await _context.GoodNeighbours
.Where(g => g.PlantId == id)
.Include(g => g.GoodNeighbour)
.ToListAsync();
GoodPlants ownGoodPlant = goodNeighbours.FirstOrDefault(i => i.GoodNeighbour.Id == plant.Id);
Plant ownPlant = plants.FirstOrDefault(i => i.Id == plant.Id);
goodNeighbours.Remove(ownGoodPlant);
plants.Remove(ownPlant);
//populate the viewmodel
EditViewModel plantModel = new EditViewModel();
plantModel.Id = plant.Id;
plantModel.Name = plant.Name;
plantModel.AllPlants = _mapper.Map<ICollection<PlantIsSelectedViewModel>>(Plants);
foreach (var element in plantModel.AllPlants)
{
if (goodNeighbours.Any(g => g.GoodNeighbour.Id == element.id))
{
element.isGoodNeighbour = true;
}
else if (badNeighbours.Any(g => g.BadNeighbour.Id == element.id))
{
element.isBadNeighbour = true;
}
}
This desperately needs refactoring, but thats not the main issue here.
In my view I contrast the elements of AllPlants collection if it appears on either of the neighbours collections and have the checkbox set to checked or not:
<tbody>
#foreach (var item in Model.AllPlants)
{
<tr>
#if (item.isGoodNeighbour)
{
<td>
#Html.Label(item.name)
</td>
<td>
<input type="checkbox" name="#Model.AllPlants"
value="#item.id"
#(Html.Raw("checked=\"checked\""))
/>
</td>
<td>
<input type="checkbox" name="#Model.AllPlants"
value="#item.id" />
</td>
}
// else statements below for badneighbours and cases without any relation.
// ...
</tr>
}
I want to know how I can keep track of all the selected items (and unselected), get them in my editViewModel and send them back to my [HttpPost] Edit method. My current [HttpPost] method receives the same viewmodel, but the AllPlants property is empty.
How do I receive the correct data back?
Thanks in advance, I'm pretty stuck!

Add more items to the model and Session

In my shopping cart i have passed data through Session.In here i face some problem there's 2 additional fields in Shopping cart.How do i add that one to my model.Because model not bind to this view.
Models
public class CartTotal
{
public decimal SubTotal { get; set; }
public decimal DeliveryCharges { get; set; }
//Some properties here
public List<Items> items { get; } = new List<Items>();
}
public class Items
{
public int ItemId { get; set; }
public string ItemCode { get; set; }
public string ItemName { get; set; }
public string PersonalizedName { get; set; }
public string Size { get; set; }
}
Before coming to the Cart i have added the items using ItemsHolder.
ItemHolder
public CartTotal ItemsHolder
{
get
{
object ItemsSession = Session["ItemSession"] as CartTotal;
if (ItemsSession == null)
{
ItemsSession = new CartTotal();
Session["ItemSession"] = ItemsSession;
}
return (CartTotal)ItemsSession;
}
}
I added items like below in item add section,
public ActionResult AddNewItems(int ItemId,string Dir)
{
ItemsHolder.items.Add(new Items() {
ItemId = items.ItemId,
ItemName = items.ItemName,
ItemPrice = items.ItemPrice,
ImageUrl = items.ImageUrl,
ItemCode = items.ItemCode
});
return RedirectToAction("Index", "Home");
}
NOW the problem is I have use that ItemSession in the Cart and in the Items dto has 2 properties called PersonalizedName & Size those fields filled it on the cart section.and i need to add those things to the ItemHolder.How do i do it?
my _cart Partial View
#{
var list = Session["ItemSession"] as XYZ.Domain.CartTotal;
}
#using (Html.BeginForm("CartFinalize", "Home"))
{
#foreach (var item in list.items)
{
// Some Codes here
<td class="cart-itm">
<input type="text" class="form-control personalizedName" placeholder="Personalized Name">
</td>
<td class="cart-itm">
<input type="text" class="form-control size" placeholder="Size">
</td>
}
}
I need to add those 2 items to the Specific item in the Cart.

Get TextBox's value in compliance with selected RadioButton asp.net.mvc-4

I need to create a view with a form where is a group of RadioButtons with TextBox for each of them. Then I want the Model to be filled with value in TextBox and value of selected RadioButton. I faced to the problem that I can't send value of coresponding to selected RadioButton TextBox.
It looks like this
There is my Model
public class CertainAnswersViewModel
{
public int SelectedValue { get; set; }
public string TextAnswer { get; set; }
public bool IsInput { get; set; }
public List<CertainAnswer> CertainAnswers { get; set; }
}
View
#using MY_BUKEP.Areas.Survey.Models;
#model CertainAnswersViewModel
#using (Html.BeginForm())
{
foreach (var answer in Model.CertainAnswers)
{
#Html.RadioButtonFor(m => m.SelectedValue, answer.IdOption, new { id = "" })
#Html.TextBoxFor(m => m.TextAnswer);
<br />
}
<input type="submit" />
}
And there are two methods in Controller
[HttpGet]
public ViewResult Test()
{
CertainAnswer ca1 = new CertainAnswer() { IdOption = 1 };
CertainAnswer ca2 = new CertainAnswer() { IdOption = 2 };
CertainAnswersViewModel cavm = new CertainAnswersViewModel();
cavm.CertainAnswers = new List<CertainAnswer>();
cavm.CertainAnswers.Add(ca1);
cavm.CertainAnswers.Add(ca2);
return View("TestView", cavm);
}
[HttpPost]
public void Test(CertainAnswersViewModel cavm)
{
Answer a = new Answer();
a.val = cavm.TextAnswer;
a.idOption = cavm.SelectedValue;
}
Below is supposed result that I would like to achive
Would appreciate any help!
You current implementation is creating duplicate textboxes for the same property and when you submit the form only the value of the first textbox will be bound (if the user was to select the 3rd option and fill in the associated textbox, the value of TextAnswer will be null because the textbox associated with the 2nd option is empty. In addition your model(s) cannot generate the view you have shown in the second image because each CertainAnswer property also requires a value to indicate if an associated textbox is required (I'm assuming for some options, it may not be).
Your models would need to be (not I have change some class and property names to better describe what they represent)
public class PossibleAnswerVM
{
public int ID { get; set; }
public string Description { get; set; }
public bool RequireAdditionalText { get; set; }
}
public class QuestionVM
{
public int ID { get; set; }
public string Description { get; set; }
[Required(ErrorMesage = "Please select an option")]
public int SelectedAnswer { get; set; }
public string AdditionalText { get; set; }
public IEnumerable<PossibleAnswer> PossibleAnswers { get; set; }
}
and the code in your controller
QuestionVM model = new QuestionVM()
{
ID = 1,
Description = "If you could return to the past, what would you choose?",
PossibleAnswers = new List<PossibleAnswer>()
{
new PossibleAnswer(){ ID = 1, Description = "Apply to another university" },
new PossibleAnswer(){ ID = 2, Description = "Apply to the same ...", RequireAdditionalText = true }
}
};
return View(model);
and the view
#model QuestionVM
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.Question.ID)
#Html.DisplayFor(m => m.Question.Description)
foreach(var option in Model.PossibleAnswers)
{
<label>
#Html.RadioButtonFor(m => m.SelectedAnswer, option.ID, new { id = "" data_textrequired = option.RequireAdditionalText })
<span>#option.Description</span>
</label>
}
#Html.ValidationMessageFor(m => m.SelectedAnswer)
#Html.TextBoxFor(m => m.AdditionalText)
<input type="submit" value="Save" />
}
Note that a data-textrequired has been added so that you could use javascript to show/hide the textbox based on the selected option. You could also use javascript to position the textbox adjacent the selected option if necessary

Paging on View with MVC Paged List

I wanna implement MVC paging so on the Index Action its working.
public ActionResult Index(int? page)
{
using (NorthwindEntities db = new NorthwindEntities())
{
CustomersViewModel model = new CustomersViewModel();
//model.Customers = db.Customers.OrderBy(m => m.CustomerID).OrderByDescending(m=>m.CustomerID).Take(10).ToList();
model.Customers = db.Customers.OrderBy(m => m.CustomerID).OrderByDescending(m=>m.CustomerID).Take(10).ToList().ToPagedList(page ?? 1,5);
model.SelectedCustomer = null;
var list = new List<int>();
for (int i = 1; i <= 20; i++)
{
list.Add(i);
}
SelectList selectedList = new SelectList(list);
ViewBag.DdList = selectedList;
//model.Countries = db.Countries.ToList();
model.CountryList = new SelectList(BLDDLCountry.GetCountry(), "CountryId", "CountryName");
model.DisplayMode = "WriteOnly";
return View(model);
}
}
Now on the View
#Html.PagedListPager(Model, page => Url.Action("Index", new {page, pagesize = 5 }))
Is accepted only if i decorate my View Model with IPagedList
#model PagedList.IPagedList<SingleCRUD.Models.CustomersViewModel>
Now as I am using
public IEnumerable<Customer> Customers { get; set; }
On My ViewModdel
The View is not accepting the Customers
#{
foreach (var item in Model.Customers)
{
if (Model.SelectedCustomer != null)
{
if (item.CustomerID ==
Model.SelectedCustomer.CustomerID)
{
#:<tr class="SelectedCustomer">
}
else
{
#:<tr>
}
}
else
{
#:<tr>
}
<td>#item.CustomerID</td>
<td>#item.CompanyName</td>
#*<td><input type="submit"
formaction="/home/select/#item.CustomerID"
value="Select" /></td>*#
<td><input type="submit"
formaction="/home/Edit/#item.CustomerID"
value="Edit" /></td>
<td></td>
#:</tr>
}
}
And Go to definition has stopped on Customers after changing the name space.
My View Model
public class CustomersViewModel
{
public int CustomerID { get; set; }
public string CompanyName { get; set; }
public string ContactName { get; set; }
public string ContactTitle { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Region { get; set; }
public Nullable<int> PostalCode { get; set; }
public string Country { get; set; }
public Nullable<int> Phone { get; set; }
public Nullable<int> Fax { get; set; }
public IEnumerable<Customer> Customers { get; set; }
public Customer SelectedCustomer { get; set; }
public string DisplayMode { get; set; }
public List<Country> Countries { get; set; }
public SelectList CountryList { get; set; }
}
So I am facing issue at the view level how do I correctly fix it.
Tried these changes
Model
public PagedList<Customer> Customers { get; set; }
View
#model SingleCRUD.Models.CustomersViewModel
#using PagedList;
#using PagedList.Mvc;
#Html.PagedListPager(Model.Customers, page => Url.Action("Index", new { page, pagesize = 5 }))
Action
model.Customers = (PagedList<Customer>)db.Customers.OrderBy(m => m.CustomerID).ToPagedList(page ?? 1, 5);
Had to explicitly convert it to Paged List as there was a conversion error not sure whether its correct.
Run Time error on View.
'System.Web.Mvc.HtmlHelper' does not contain a definition for 'PagedListPager' and the best extension method overload 'PagedList.Mvc.HtmlHelper.PagedListPager(System.Web.Mvc.HtmlHelper, PagedList.IPagedList, System.Func)' has some invalid arguments
Error
Error 1 Cannot implicitly convert type 'PagedList.IPagedList' to 'PagedList.PagedList'. An explicit conversion exists (are you missing a cast?)
Using
#Html.PagedListPager(Model.Customers, page => Url.Action("Index", new { page, pagesize = 5 }))
on View tried writing this in the form tag as well as out side the form tag.
Its a bit unclear what you claiming. #model PagedList.IPagedList<CustomersViewModel> will not work since your model is CustomersViewModel but it will work if your use #model CustomersViewModel.
If you wanting to display a paged list of Customer, then your model property needs to be
public IPagedList<Customer> Customers { get; set; }
and in the view use
#Html.PagedListPager(Model.Customers, page => Url.Action("Index", new {page, pagesize = 5 }))

Categories