How to make a viewModel to be Enumerable - asp.net-core

I'm implementing asp.net core 3.1. I'm passing a viewmodel to the razor view which is in the following:
public class BuyRequestViewModel
{
public IEnumerable<BuyRequest> BuyRequestVM { get; set; }
public IEnumerable<string> PlatesVM { get; set; }
}
My problem is, as my viewmodel is not of type Ienumerable, I'm getting error in foreach in the razor view. The error is like the following:
ForEach statement can not operate on variables of type 'MyPanel.ViewModels.BuyRequestViewModel' because 'MyPanel.ViewModels.BuyRequestViewModel' does not contain a public instance definition for 'GetEnumerator'
And my razor view code is like below:
#model MyPanel.ViewModels.BuyRequestViewModel
<table id="myDummyTable" class="table m-table mytable table-striped table-bordered">
<thead>
<tr>
<th>
Region
</th>
<th>
Zone
<th>
MyPlate
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.BuyRequestVM.Select(x => x.Region))
</td>
<td>
#Html.DisplayFor(modelItem => item.BuyRequestVM.Select(x => x.Zone))
</td>
<td>
#Html.DisplayFor(modelItem => item.PlatesVM)
</td>
</tr>
}
</tbody>
</table>
And here below is the Index action in my controller:
public async Task<IActionResult> Index()
{
var bwrvm = new BuyRequestViewModel();
List<string> platesList = new List<string>();
var WasteAPIContext = _context.BuyRequest
.Include(b => b.UserWasteUnitNavigation).ToList();
bwrvm.BuyWasteRequestVM = WasteAPIContext;
var plateData = _context.Car.Select(x => x.Plate).ToList();
for (int i = 0; i < plateData.Count; i++)
{
//platesList.Add(plateData[i].ToString().Substring(2, 5));
string temp = getPlateCharacter(plateData[i].ToString().Substring(2, 3));
plateData[i].Remove(2, 3);
string totalPlate = plateData[i].Insert(2, temp);
platesList.Add(totalPlate);
}
bwrvm.PlatesVM = platesList;
return View(bwrvm);
}
I appreciate of any help.

#foreach (var item in Model)
should be
#foreach (var item in Model.BuyWasteRequestVM)
because Model == BuyRequestViewModel which contains 2 properties with lists.

Related

Update Partial view MVC4

I have this controller:
public ActionResult PopulateTreeViewModel()
{
MainModelPopulate mainModelPopulate = new MainModelPopulate();
// populate model
return View(mainModelPopulate);
}
That has a view like this:
#model xxx.xxx.MainModelPopulate
<table>
#foreach (var item2 in Model.CountryList)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item2.CountryName);
</td>
</tr>
foreach (var item3 in item2.BrandList)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item3.BrandName);
</td>
</tr>
foreach (var item4 in item3.ProductList)
{
<tr>
<td>
#Html.ActionLink(item4.ProductName, "FunctionX", new { idLab = item3.BrandID, idDep = item4.ProductID });
</td>
</tr>
}
}
}
</table>
The FunctionX controller is like this :
public ActionResult FunctionX(int idBrand=1 , int idProd=1)
{
List<ListTypeModel> typeModelList = new List<ListTypeModel>();
// populate typeModelList
return PartialView(typeModelList);
}
}
with this partial view:
#model IEnumerable<TControl.Models.ListTypeModel>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
</tr>
}
</table>
I want to add this partial view in my main view (PopulateTreeViewModel) and update the table with the relative type of product contained in Function X.
I tried also to substitute #Html.ActionLink with #Ajax.ActionLink and it performs the same way.
Have you tried #Html.RenderAction(item4.ProductName, "FunctionX", new { idLab = item3.BrandID, idDep = item4.ProductID });
There are other options too..! Pls refer http://www.dotnet-tricks.com/Tutorial/mvc/Q8V2130113-RenderPartial-vs-RenderAction-vs-Partial-vs-Action-in-MVC-Razor.html

Fetch id for viewpage

FIXED
what i want to do is a viewpage that contains accountslist by clicking on any of account name it should open selected accountid Payablerecords and Reciveablerecords. Note:Payables and Receivables are two Propertiest Taken From same DataModel tbl_Transaction(Which is collection).So can i get Id for collections?
I am getting the Selected AccountID records in Payable view but I cant get the same id related record in Reciveable view plz help me out.
Here is the code.
public class AccountsController : Controller
{
private AccountBs objBs;
public AccountsController()
{
objBs = new AccountBs();
}
// GET: Shinwari/Accounts
public ActionResult Index()
{
var accounts = objBs.GetALL();
return View(accounts);
}
<%=#model IEnumerable<BOL.tbl_Accounts>
#{
ViewBag.Title = "Index";
}
<h2>Accounts</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Contact)
</th>
<th>
#Html.DisplayNameFor(model => model.Discription)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.ActionLink(item.Name, "Index", "AccountDetailPayable", new {accountid=item.AId },null)
#*#Html.DisplayFor(modelItem => item.Name)*#
</td>
<td>
#Html.DisplayFor(modelItem => item.Contact)
</td>
<td>
#Html.DisplayFor(modelItem => item.Discription)
</td>
}
</table>%>
ReciveableAndPayablecontroller
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);
Veiw
#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>
**Note Payable and reciveable both views are identical **
TO have same table work like 2 different properties you need to create new model class, In my case it is as following..
public ADetailsVm
{
List<tbl_Transiction>Payables{get;set;}
List<tbl_Transiction>Reciveables{get;set;}
}
And then add the following class to DBContext Class ... it would be like
public DbSet<ADetailsVm>ADetailsVm{get;set;}
You have to create new class where you need to add two lists as following.
public NewClass
{
List<tbl_Transiction>Payables{get;set;}
List<tbl_Transiction>Reciveables{get;set;}
}
and Add the class to your DbContext Class as follows.
public DbSet<NewClass>PayablesAndReceiveables{get;set;}
And loop your newly created lists objects in View where you need them.

Insert Partial View to a another Partial View

This is the main view Dept_Manager_Approval.cshtml where I have put a modal to show data.
<td>
<i title="View Details">
#Ajax.ActionLink(" ", "ViewAccessStatus", new { id = item.request_access_id },
new AjaxOptions
{
HttpMethod = "Get",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "edit-div",
}, new { #class = "fa fa-eye btn btn-success approveModal sample" })</i>
</td>
In this partial view which just a modal, ViewAccessStatus.cshtml , I have inserted in here another partial view.
<div>
<h2><span class ="label label-success">Request Creator</span> </h2>
#if (Model.carf_type == "BATCH CARF")
{
#Html.Partial("Batch_Requestor1", new {id= Model.carf_id })
}else{
<h4><span class ="label label-success">#Html.DisplayFor(model=>model.created_by)</span></h4>
}
</div>
COntroller:
public ActionResult Batch_Requestor1(int id = 0)
{
var data = db.Batch_CARF.Where(x => x.carf_id == id && x.active_flag == true).ToList();
return PartialView(data);
}
Batch_Requestor1.cshtml
#model IEnumerable<PETC_CARF.Models.Batch_CARF>
#{
ViewBag.Title = "All Requestors";
}
<br/><br/>
<table class="table table-hover">
<tr class="success">
<th>
#Html.DisplayName("Full Name")
</th>
<th>
#Html.DisplayName("Email Add")
</th>
<th>
#Html.DisplayName("User ID")
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.fname) - #Html.DisplayFor(modelItem => item.lname)
</td>
<td>
#Html.DisplayFor(modelItem => item.email_add)
</td>
<td>
#Html.DisplayFor(modelItem => item.user_id)
</td>
</tr>
}
</table>
When I run this, I've got this error
The model item passed into the dictionary is of type '<>f__AnonymousType01[System.Int32]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[PETC_CARF.Models.Batch_CARF]'.
Any ideas how will I insert another partial view?
#Html.Partial() renders a partial view. It does not call an action method that in turn renders the partial. In your case
#Html.Partial("Batch_Requestor1", new {id= Model.carf_id })
is rendering a partial view named Batch_Requestor1.cshtml and passing it a model defined by new {id= Model.carf_id } (and anonymous object) but that view expects a model which is IEnumerable<PETC_CARF.Models.Batch_CARF>.
Instead, you need to use
#Html.Action("Batch_Requestor1", new {id= Model.carf_id })
which calls the method public ActionResult Batch_Requestor1(int id = 0) and passes it the value of Model.carf_id, which will in turn render the partial view.

MVC 4 - Return error message from Controller - Show in View

I am doing a C# project using Razor in VS2010 (MVC 4).
I need to return an error message from Controller to View and show it to the user.
What I have tried:
CONTROLLER:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
model.error_msg = model.update_content(model);
ModelState.AddModelError("error", "adfdghdghgdhgdhdgda");
ViewBag.error = TempData["error"];
return RedirectToAction("Form_edit", "Form");
}
VIEW:
#model mvc_cs.Models.FormModels
#using ctrlr = mvc_cs.Controllers.FormController
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
#Html.ValidationSummary("error")
#Html.ValidationMessage("error")
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
<table>
<tr>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
Please help me to achieve this.
The Return View(model) returns you error because you don't fill the model with the values in your post method and the model data for the dropdown is empty. Please provide the Get method to explain further how to manage displaying the error. In order to the error to be shown you should use this:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
if(ModelState.IsValid())
{
--- operations
return Redirect("OtherAction", "SomeController");
}
// here you can use a little trick
//fill the model property that holds the information for the dropdown with the data
// you haven't provided the get method but it should look something like this
model.Countries = ... some data goes here;
model.dd_value = ... some other data;
model.dd_text = ... other data;
ModelState.AddModelError("", "adfdghdghgdhgdhdgda");
return View(model);
}
and then in the view just use :
#model mvc_cs.Models.FormModels
#using ctrlr = mvc_cs.Controllers.FormController
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
#Html.ValidationSummary(true)
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
<table>
<tr>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
This should work okay.
If you just use RedirectToAction it will redirect you to the get method --> you will have no error but the view will be just reloaded and no error would be shown.
other way around is that you can pass the error not by ModelState.AddError, but with ViewData["error"] like this:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
TempData["error"] = "someErrorMessage";
return RedirectToAction("form_Post", "Form");
}
[HttpGet]
public ActionResult form_edit()
{
do stuff here ----
ViewData["error"] = TempData["error"];
return View();
}
#model mvc_cs.Models.FormModels
#using ctrlr = mvc_cs.Controllers.FormController
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
<div>#ViewData["error"]</div>
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
<table>
<tr>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
Thanks for all the replies.
I was able to solve this by doing the following:
CONTROLLER:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
model.error_msg = model.update_content(model);
return RedirectToAction("Form_edit", "Form", model);
}
public ActionResult form_edit(FormModels model, string searchString,string id)
{
string test = model.selectedvalue;
var bal = new FormModels();
bal.Countries = bal.get_contentdetails(searchString);
bal.selectedvalue = id;
bal.dd_text = "content_name";
bal.dd_value = "content_id";
test = model.error_msg;
ViewBag.head = "Heading";
if (model.error_msg != null)
{
ModelState.AddModelError("error_msg", test);
}
model.error_msg = "";
return View(bal);
}
VIEW:
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
#ViewBag.error
#Html.ValidationMessage("error_msg")
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
}
If you want to do a redirect, you can either:
ViewBag.Error = "error message";
or
TempData["Error"] = "error message";
You can add this to your _Layout.cshtml:
#using MyProj.ViewModels;
...
#if (TempData["UserMessage"] != null)
{
var message = (MessageViewModel)TempData["UserMessage"];
<div class="alert #message.CssClassName" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<strong>#message.Title</strong>
#message.Message
</div>
}
Then if you want to throw an error message in your controller:
TempData["UserMessage"] = new MessageViewModel() { CssClassName = "alert-danger alert-dismissible", Title = "Error", Message = "This is an error message" };
MessageViewModel.cs:
public class MessageViewModel
{
public string CssClassName { get; set; }
public string Title { get; set; }
public string Message { get; set; }
}
Note: Using Bootstrap 4 classes.

Modelmetadata additional values always empty MVC4

I have the below property in my model:
[Required]
[UIHint("DropDownList")]
[AdditionalMetadata("Source", "Party.Organization.Caption")]
public int PartyId { get; set; }
I am trying to get the additional metadata value in view as follows:
object s = ViewData.ModelMetadata.AdditionalValues["Source"];
but it is always returning count 0.
not sure, why, can somebody advise pls?
complete view:
#model IEnumerable<object>
#using System.Reflection;
#using r2d2Web.Extensions;
#using d2Utils.Extensions.d2Type;
#using d2Utils.Reflection;
#using System.Collections;
#{
Type mdlType = Model.First().GetType();
PropertyInfo keyProp = mdlType.GetKeyProperty();
IEnumerable<PropertyInfo> props = mdlType.EditorProps();
Hashtable parties = (Hashtable)ViewData["Parties"];
Hashtable partyroles = (Hashtable)ViewData["Partyroles"];
}
<div class="grid">
<table>
<thead>
<tr>
#foreach (var prop in props)
{
<th>#prop.Name</th>
}
<th></th>
</tr>
</thead>
<tbody>
#foreach (var obj in Model)
{
<tr>
#foreach (var prop in props)
{
if (prop.Name == "PartyId")
{
object s = ViewData.ModelMetadata.AdditionalValues["Source"];
<td>#(obj.GetValForProp<string>(s.ToString()))</td>
}
else if (prop.Name == "PartyRoleTypeId")
{
<td>#partyroles[obj.GetValForProp<int>(prop.Name)]</td>
}
else
{
<td>#(obj.GetValForProp<string>(prop.Name))</td>
}
}
<td>
#Html.ActionLink("Edit", "Edit", new { id = obj.GetValForProp<int>(keyProp.Name) }) |
</td>
</tr>
}
</tbody>
</table>
</div>
<div id="my-dialog"></div>
You are not specifying field for getting additional metadata. Try to do it like this:
#ViewData.ModelMetadata.Properties.FirstOrDefault(n => n.PropertyName == "PartyId").AdditionalValues["Source"]
If you had strongly typed view model with type that contains PartyId better option would be to use
#ModelMetadata.FromLambdaExpression(x => x.PartyId, ViewData).AdditionalValues["Source"]