Child foreach not looping as intended in Partial using ViewModel - c#

I have a List of Recipes and each Recipe has a number of RecipeLines, I want to loop Each Recipe with it's corresponding Recipe Lines foreach Looping inside of the parent Recipe foreach.
Recipe Class
namespace XXX.Models
{
public class Recipe
{
public int RecipeID { get; set; }
public string RecipeName { get; set; }
public string RecipeInstructions { get; set; }
public virtual List<RecipeLine> RecipeLines { get; set; }
}
}
RecipeLine Class
namespace XXX.Models
{
public class RecipeLine
{
public int RecipeLineID { get; set; }
public float Quantity { get; set; }
public int MeasurementID { get; set; }
public int RecipeID { get; set; }
public int IngredientID { get; set; }
public virtual Measurement Measurement { get; set; }
public virtual Recipe Recipe { get; set; }
public virtual Ingredient Ingredient { get; set; }
}
}
No need to list the Ingredient and Measurement Class, but they are structured fine.
Now let's look at the ViewModel
namespace XXX.ViewModels
{
public class RecipeLineViewModel
{
public IEnumerable<Recipe> Recipes { get; set; }
public IEnumerable<RecipeLine> RecipeLines { get; set; }
}
}
And the PartialsController
namespace XXX.Controllers
{
public class PartialsController : Controller
{
private XXXDb db = new XXXDb();
public ActionResult RecipeList()
{
RecipeLineViewModel viewModel;
viewModel = new RecipeLineViewModel();
viewModel.Recipes = db.Recipes.ToList();
viewModel.RecipeLines = db.RecipeLines.Include(r =>
r.Measurement).Include(r => r.Ingredient);
return PartialView("_RecipeList", viewModel);
}
}
}
Now the partial view is Views > Partials > __RecipeList.cshtml
RecipeList Partial View
#model XXX.ViewModels.RecipeLineViewModel
#foreach (Recipe recipe in Model.Recipes)
{
<div>#recipe.RecipeName</div>
<div>#recipe.RecipeInstructions</div>
foreach (RecipeLine recipeLines in Model.RecipeLines)
{
<div class="row">
<div class="large-12 columns">
#recipeLines.Quantity #recipeLines.Measurement.MeasurementEn
#recipeLines.Ingredient.IngredientNameEn
</div>
</div>
}
}
}
(I printed the RecipeID at the end of each recipeLine to show that each recipe is repeating out Recipe Lines for RecipeID = 1 and not their Lines related to their own RecipeID. I'm a newbie to this stuff, I'm assuming that somewhere I should be telling the Recipe Lines about the RecipeID they should be looping for????
Here is the result I'm getting:

In your Recipie model you have have a List<RecipieLine> property which one would assume is to maintain one or more RecipieLine model objects. In your RecipieLineViewModel you have two separate IEnumerable collections exposing your Recipie and RecipieLine objects.
In your actual view, you are iterating over your Recipie collection:
#foreach (Recipe recipe in Model.Recipes)
Looks good, however, the following line is your issue:
foreach (RecipeLine recipeLines in Model.RecipeLines)
This enumerates over the RecipieLine model objects stored in your RecipieLineViewModel RecipieLine collection, however, it currently has no way of knowing which Recipie you are currently enumerating in your outer foreach.
You either want to provide a where LINQ clause of some sort using the data available to you from the current Recipie object enumeration, or alter the inner foreach to use the current Recipie enumeration rather than the Model.
Update
For completeness, here is an example of how to use the current Recipe object enumeration to access its associated RecipieLines:
#forech (var recipie in Model.Recipies)
{
<div>#recipe.RecipeName</div>
<div>#recipe.RecipeInstructions</div>
foreach (var recipieLine in recipie.RecipieLines)
{
<div class="row">
<div class="large-12 columns">
// Do something with recipieLine here
</div>
</div>
}
}
The above would be my preferred method as it is more efficient that performing a LINQ query each enumeration, and removes the IEnumerable<RecipieLine> from your ViewModel which is effectively duplicate code.

Add a where Linq clause to foreach.
#foreach (Recipe recipe in Model.Recipes)
{
<div>#recipe.RecipeName</div>
<div>#recipe.RecipeInstructions</div>
foreach (RecipeLine recipeLines in Model.RecipeLines.Where(rl => rl.RecipeID == recipe.RecipeID))
{
Upd:
Also you can definently include RecipeLine stuff in Recipes and you won't need RecipeLine Collection at all! Just loop through recipe.RecipeLines
Something like this:
viewModel.Recipes = db.Recipes.Include("RecipeLine").Include("RecipeLine.Measurement").
.Include("RecipeLine.Ingredient");

Related

Auto Generate select from list in model

I have the following sample model:
public class FooModel
{
public List<Bar> myList { get; set; }
}
public class Bar
{
public string description { get; set; }
public int pos { get; set; }
public int myField { get; set; }
}
And, in my cshtml file I have this:
#foreach (var bar in Model.myList)
{
<div class="row">
<div class="col-2">PLACEHOLDER</div>
</div>
...
}
So basically, the field myField will be a match (when it has some value set) to a pos of another item. What I want, is to autogenerate a dropdown in PLACEHOLDER containing all the descriptions (and pos as value), of all Bar, with the one that matches selected.
I tried this approach (sample) but this shouldn't work, since I am in a foreach inside my Model
#Html.DropDownListFor(col=> col.myField, new SelectList(Model.myList.description, "Id", "Text"), "Choose... ")
Can I do this automatically or will I have to create the dropdown manually?
Thanks!

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!

How to access Dictionary Model Property value from View - ASP.Net MVC

I want to get value of Dictionary from View.
In the view, I have a main/first foreach loop that retrieve data from Model, and inside the main/first loop, I need to retrieve the ListAttribute value according to the first loop by Id.
**//Code in the View - First loop to retrieve data from Model**
#model IEnumerable<ABC.Web.Models.Room.Housing>
foreach (var item in Model.OrderBy(x => x.Id))
{
<div class="col-xs-18 col-sm-4">
<div class="thumbnail">
*...(remainer of the code)*
//Here to insert second loop to retrieve *ListAttribute*
}
//Code in the Model
namespace ABC.Web.Models.Room
{
public class Housing
{
public string[] FloorPlan { get; set; }
public Dictionary<string, ListAttribute> ListAttributes { get; set;}
public string Latitude { get; set; }
}
public partial class ListAttribute
{
public string name { get; set; }
public string icon { get; set; }
public string value { get; set; }
}
}
If item is an object of type Housing, then you can do this:
foreach (KeyValuePair<string, ListAttribute> listItem in item.ListAttributes)
{
// Then you will have...
// listItem.Key -> string
// listItem.Value -> ListAttribute
}
Source

ASP.NET MVC - ViewModel nested loop

I am new to ASP.NET MVC and trying to list some companies, then all contacts under that each company. I think I am getting close to make it work, so please help if you can.
The model for table and field name:
namespace ERP.Models
{
[Table("ERP_Company")]
public class ERP_Company
{
[Key]
public int CompanyID { get; set; }
public string Name { get; set; }
}
[Table("ERP_CompanyContact")]
public class ERP_Contact
{
[Key]
public int ContactID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int CompanyID { get; set; }
}
}
These are the methods getting the Company and Contact list:
namespace ERP.Models
{
public class Method1
{
public ERPEntities db = new ERPEntities();
public List<ERP_Company> getCompanyList()
{
List<ERP_Company> companyList = (
from c in db.ERP_Company
where c.Name.Contains("Network")
select c).Take(10).ToList();
return companyList;
}
// This below method needs to get the passing CompanyID from getCompanyList for filtering.
public List<ERP_Contact> getContactList()
{
List<ERP_Contact> contactList = (
from cc in db.ERP_CompanyContact
select cc).Take(50).ToList();
return contactList;
}
/* Tried this below, but not work for the Controller, maybe I am doing wrong.
public List<ERP_Contact> getContactList(int CompanyID)
{
List<ERP_Contact> contactList = (
from cc in db.ERP_CompanyContact
where cc.CompanyID == CompanyID
select cc).Take(50).ToList();
return contactList;
}
*/
}
}
Use ViewModel (suggested from other post), combine both models:
namespace ERP.Models
{
public class ViewModelDemoVM
{
public List<ERP_Company> allCompanies { get; set; }
public List<ERP_Contact> allContacts { get; set; }
}
}
This code in the Controller:
Method1 _repository = new Method1();
public ActionResult ViewModelDemo()
{
ViewModelDemoVM vm = new ViewModelDemoVM();
vm.allCompanies = _repository.getCompanyList();
vm.allContacts = _repository.getContactList();
return View(vm);
}
Lastly, the view code:
#model ERP.Models.ViewModelDemoVM
#{
ViewBag.Title = "ViewModelDemo";
}
<h2>ViewModelDemo</h2>
<ul>
#foreach (var company in Model.allCompanies)
{
<li>#company.CompanyID | #company.Name</li>
<ul>
<!-- HERE is I want to put the filtering... foreach contact WHERE CompanyID = Model.allCompanies.CompanyID-->
#foreach (var contact in Model.allContacts)
{
<li>#contact.ContactID | #contact.FirstName</li>
}
</ul>
}
</ul>
How can I filter the contacts (2nd loop) based on the #company.CompanyID? Sample code would be appreciated.
Thanks in advance.
you can apply where clause in second loop. try below code. i hope this helps
#model ERP.Models.ViewModelDemoVM
#{
ViewBag.Title = "ViewModelDemo";
}
<h2>ViewModelDemo</h2>
<ul>
#foreach (var company in Model.allCompanies)
{
<li>#company.CompanyID | #company.Name</li>
<ul>
<!-- HERE is I want to put the filtering... foreach contact WHERE CompanyID = Model.allCompanies.CompanyID-->
#foreach (var contact in Model.allContacts.Where(x=>x.CompanyId ==company.CompanyID)
{
<li>#contact.ContactID | #contact.FirstName</li>
}
</ul>
}
</ul>
You should create a view model with nested structure and use that. Remember, view models are specific to the view. So build it as your view needs it.
public class CompanyVm
{
public string Name { set; get; }
public IEnumerable<ContactVm> Contacts { set;get;}
}
public class ContactVm
{
public string Name { set; get; }
}
public class ViewModelDemoVM
{
public List<CompanyVm> Companies { set; get; }
}
Your Contact table/entity already has a foriegn key/navigational property to Company entity/table. So all you have to do is, get the companies and it's corresponding customers,map it to our view model and use it in the view.
Add a collection type to Company entity class to access it's contacts.
public class ERP_Company
{
[Key]
public int CompanyID { get; set; }
public string Name { get; set; }
public ICollection<Contact> Contacts { set; get; }
}
Now in your action method, you can get the data
public IActionResult ViewModelDemo()
{
var vm = new ViewModelDemoVM();
vm.Companies = db.Companies
.Select(a => new CompanyVm { Name = a.Name,
Contacts = a.Contacts
.Select(c => new ContactVm
{ Name = c.Name })}
).ToList();
return View(vm);
}
Now in your view, just loop through the company and for each company, loop through it's contacts
#model ViewModelDemoVM
#foreach(var company in Model.Companies)
{
<h3>#company.Name</h3>
<h5>Contacts</h5>
#foreach(var contact in company.Contacts)
{
<p>#contact.Name</p>
}
}
Some notes
Create view models as needed by view
Do not mix entity classes (used by ORM) with view models
Keep less logic/C# code in views
I used class generic class names (Contact instead ERP_Contact) and property names. When you use the above code, make the needed changes to use your existing names if needed.

.net mvc declare a property to hold a collection of generic objects?

I have an MVC proyect using EF (database first) and I have already created CRUD for some entities.
Now I am trying to create a dashboard page that contains widgets or similar, each listing the last 10 entities from different db tables (last 10 products created, last 10 customers, etc)
To create the widget I have followed this tutorial https://www.codeproject.com/Articles/598383/Widgets-in-MVC
So I have 2 interfaces and 2 classes that implements those interfaces:
public interface IWidget
{
int SortOrder { get; set; }
string Entity { get; set; }
string CssClassName { get; set; }
string HeaderText { get; set; }
string FooterText { get; set; }
ISubWidget SubWidget { get; set; }
}
public interface ISubWidget
{
ICollection<Products> EntitiesList { get; set; }
}
public class Widget : IWidget
{
public int SortOrder { get; set; }
public string Entity { get; set; }
public string HeaderText { get; set; }
public string FooterText { get; set; }
public ISubWidget SubWidget { get; set; }
}
public class SubWidget : ISubWidget
{
public ICollection<Products> EntitiesList { get; set; }
}
then I have 3 partial views: one for the widget itself, one for the subwidget, and one that will act as a container of all created widgets.
#model Proyect.Comun.Widget.IWidget
<div class="widget">
<div class="#Model.CssClassName">
<div class="widgetHeader">
#Model.HeaderText
</div>
<div class="widgetBody">
#Html.Partial(string.Concat(new[] { "Widget", "/", "_SubWidget" }), #Model.SubWidget)
</div>
<div class="widgetFooter">
#Model.FooterText
</div>
</div>
</div>
#model Proyect.Comun.Widget.ISubWidget
<table>
#foreach (var item in Model.EntitiesList)
{
<tr>
<td> #Html.DisplayFor(modelItem => item.product_number) </td>
<td> #Html.DisplayFor(modelItem => item.product_name) </td>
<td> #Html.DisplayFor(modelItem => item.product_description) </td>
</tr>
}
</table>
#model Fruterias.Comun.Widget.IWidget
#foreach (Proyect.Comun.Widget.IWidget wm in ViewBag.Widgets)
{
#Html.Partial(string.Concat(new[] { "Widget", "/", "_Widget" }), wm)
}
then in the Dashboard controller:
public ActionResult Index()
{
ViewBag.Widgets = GetWidgetData();
return View();
}
public List<IWidget> GetWidgetData()
{
var lastWidget = new List<IWidget>{
new Widget()
{
SortOrder = 1,
CssClassName = "high",
Entity = "Products",
HeaderText = "Last Products",
FooterText = "",
SubWidget = new SubWidget {
EntitiesList = db.products.OrderByDescending(p => p.date).Take(10).ToList(),
}
},
};
return lastWidget;
}
And finally in the view for Dashboard/Index:
<p>
#{
#Html.Partial("Widget/_WidgetContainer");
}
</p>
This works ok and shows a list of Product entities. But now I want to create different widgets associated to different entities.
Of course the problem is that the property EntitiesList is declared as a collection of Product objects, so I can not fill it with, for example, customers objects.
What would be the solution here? I could create different interfaces (and classes) for each type of entity, but Im sure there must be a better way...
Also, as the entities models (Products, Customers...) are generated with Entity Framework db first, I guess I can not create an Interface and make them implement that interface (or can I?)... (anytime the db changes and models are regenerated by EF, all that would be lost?)
If you don't need to have the EntitiesList to be a specific type you could just have the SubWidget be generic and set its type when you instantiate.
public class SubWidget<T> : ISubWidget<T> where T : class
{
public ICollection<T> EntitiesList { get; set; }
}
or you could use dynamic object.
public class SubWidget : ISubWidget
{
public ICollection<dynamic> EntitiesList { get; set; }
}

Categories