Hi I am trying to create a dynamic menu an I seem to have gotten into some trouble.
The problem is that when run the application only the items belonging to the last category get displayed repeatedly for all categories.
There are two places where I might have made the mistake but I can not be sure wicth is the place I made it I will also post a diagram of the two tables I am working with.This is the diagram:
I am using LINQ TO SQL to acces the database and have created a separate class to do this.Here is the code for accesing the data.This is the first place where I might have made the mistake when adding the items to the Dictionary aldo I stepped over this method with the debugger and it seems to be ok I can not be sure so I posted it:
public Dictionary<string , List<string>> subCatByCatList() {
Dictionary<string , List<string>> SubcatByCat = new Dictionary<string , List<string>>();
var subcategoriesByCategory = from category in dataContext.Categories
join subcategory in dataContext.SubCategories
on category.CatId equals subcategory.CatId
into cs
select new {
CategoryName = category.CatName ,
SubCategories = cs
};
List<string> subcategories = new List<string>();
foreach( var category in subcategoriesByCategory ) {
string CategoryName = category.CategoryName;
subcategories.Clear();
foreach( var subCategory in category.SubCategories ) {
subcategories.Add(subCategory.SubCatName);
}
SubcatByCat.Add(CategoryName , subcategories);
}
return SubcatByCat;
}
THe seccond place is when I am trying to display the data.This is where I most likely think I made the mistake when I tryed to display it.Here is the code:
#{
ComputerStoreDataAccess data = new ComputerStoreDataAccess();
Dictionary<string, List<string>> nav = data.subCatByCatList();
<ul>
#foreach (var category in nav)
{
<li>#category.Key</li>
foreach (var subcategory in category.Value)
{
<div>
<ul>
<li>#subcategory</li>
</ul>
</div>
}
}
</ul>
}
EDIT
In this loop you have to create the subcategories object on every iteration, otherwise you have reference to single list in a SubcatByCat:
foreach( var category in subcategoriesByCategory ) {
string CategoryName = category.CategoryName;
subcategories = new List<string>(); // <-- create new object!
foreach( var subCategory in category.SubCategories ) {
subcategories.Add(subCategory.SubCatName);
}
SubcatByCat.Add(CategoryName , subcategories);
}
Related
I'd like to start off with an apology for the poorly worded question and if this or a similar question has been answered before, but I've spent the entire day and large parts of yesterday looking for an answer with no luck so hopefully this will be ok.
I'm a complete novice to MVC (and quite new to programming in general) and am working on a "TV Guide" website, currently with hardcoded schedules etc just to get design and such in place and to learn MVC. On my index-page I will list every show, divided per channel, and as such I have this in my controller. (Trying to show only the most relevant parts)
public ActionResult Index()
{
List<ChannelVM> channelList = db.Channel.Select(x => new ChannelVM
{
Id = x.Id,
Name = x.Name,
...
}).ToList();
List<ScheduleVM> scheduleList = db.Schedule.Select(x => new ScheduleVM
{
...
ChannelId = x.ChannelId,
ChannelName = x.Channel.Name,
...
}).ToList();
var indexModel = new IndexVM
{
ChannelList = channelList,
ScheduleList = scheduleList
};
return View(indexModel);
}
In my View, I have:
#model DagensTV.Models.ViewModels.IndexVM
...
#if (Model != null)
{
foreach (var item in Model.ChannelList)
{
...
<ul>
#foreach (var show in Model.ScheduleList)
{
<li>
...
</li>
}
</ul>
...
}
}
Currently only 4 channels are hardcoded with data in a local database. Schedule is the table that has all the info I want show, and it includes a foreign key to Channel, ChannelId. With the code shown above, it creates the 4 unordered lists on the view and fills the section from Model.ChannelList correctly with the logo and so on, but in the actual schedule I of course get everything listed for every channel. I've "solved it" by making a change in the view as seen below:
#foreach (var show in Model.ScheduleList.Where(x => x.ChannelName == item.Name))
However that is not a solution I'm satisfied with as I've been told over and over to avoid that kind of code in the View, and to do everything in the controller.
In the controller I've tried adding some lambda to "filter" things similar to the solution above from the view, but to no avail.
I'm at a loss on how to proceed now so my question is, how can I filter the scheduleList so that the shows with a ChannelID of 1 go to one unordered list in the view, the shows with a ChannelID of 2 go to another etc? Any help is greatly appreciated even if it's just a nudge in the right direction. :)
You can have a property in your ChannelViewModel to hold the Schedule of the channel, and from the controller action you would return each Channel with it's Schedules populated and you would then need to only loop through those.
Your ChannelVM would need to be like:
public class ChannelVM
{
public int Id {get;set;}
public string Name {get;set;}
......
......
public List<ScheduleVM> Schedules {get;set;}
}
It would be something like:
List<ChannelVM> channelList = db.Channel.Select(x => new ChannelVM
{
Id = x.Id,
Name = x.Name,
Schedules = db.Schedule.Where(s=>s.ChannelId == x.Id)
.Select(schedule => new ScheduleVM
{
...
}).ToList();
Now in your view you would be doing something like:
foreach (var item in Model.ChannelList)
{
...
<ul>
#foreach (var show in item.ScheduleList)
{
<li>
...
</li>
}
</ul>
...
}
Note: This is just to give you the approach idea how you can do it, you would need to adjust your ViewModel to get it working.
Hope it helps!
can someone tell me what I'm doing wrong? :-)
I have this simple query:
var sample = from training in _db.Trainings
where training.InstructorID == 10
select new { Something = training.Instructor.UserName };
And I pass this to ViewBag.
ViewBag.Sample = sample;
Then I want to access it in my view like this:
#foreach (var item in ViewBag.Sample) {
#item.Something
}
And I get error message 'object' does not contain a definition for 'Something'. If I put there just #item, I get result { Something = SomeUserName }
Thanks for help.
This cannot be done. ViewBag is dynamic and the problem is that the anonymous type is generated as internal. I would recommend you using a view model:
public class Instructor
{
public string Name { get; set; }
}
and then:
public ActionResult Index()
{
var mdoel = from training in _db.Trainings
where training.InstructorID == 10
select new Instructor {
Name = training.Instructor.UserName
};
return View(model);
}
and in the view:
#model IEnumerable<Instructor>
#foreach (var item in ViewBag.Sample) {
#item.Something
}
If you want to send in ViewData For example and don't want to send in model
you could use the same could as in the upper answer
and in the Controller
enter code here
ViewData[Instractor] = from training in _db.Trainings
where training.InstructorID == 10
select new Instructor {
Name = training.Instructor.UserName
};
and in the view you need to cast this to
`IEnumerable<Instructor>`
but to do this you should use
#model IEnumerable<Instructor>
Then you could do something like this
IEnumerable<instructors> Instructors =(IEnumerable<Instructor>)ViewData[Instractor];
then go with foreach
#foreach (var item in Instructors ) {
#item.Something
}
This question already has answers here:
Dynamic Anonymous type in Razor causes RuntimeBinderException
(12 answers)
Closed 6 years ago.
I'm trying to pass dynamic results to View from Controller, method ShowColor returns dynamic results. In View I try to loop through the collection but I'm getting error
'object' does not contain a definition for 'ColorID'.
I have the following code in Controller and View
public class myColor
{
public int ID { get; set; }
public string Name { get; set; }
public string Like { get; set; }
}
public dynamic ShowColor()
{
IList<myColor> color = new List<myColor>();
color.Add(new myColor { ID = 1, Name = "Red", Like = "***" });
color.Add(new myColor { ID = 2, Name = "Green", Like = "*****" });
color.Add(new myColor { ID = 3, Name = "Blue", Like = "**" });
color.Add(new myColor { ID = 4, Name = "Yellow", Like = "*" });
var select = (from c in color
select new
{
ColorID = c.ID,
ColorName = c.Name
}).ToList();
return select;
}
public ActionResult DBDynamic()
{
return View(ShowColor());
}
View
#model dynamic
#{
ViewBag.Title = "DBDynamic";
}
<h2>DBDynamic</h2>
<p>
<ul>
#foreach (var m in Model)
{
<li> #m.ColorID</li>
}
</ul>
</p>
Found the solution here and a nice blog here:
public static ExpandoObject ToExpando(this object anonymousObject)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(anonymousObject))
{
var obj = propertyDescriptor.GetValue(anonymousObject);
expando.Add(propertyDescriptor.Name, obj);
}
return (ExpandoObject)expando;
}
And call it like this
var select = (from c in color
select new
{
ColorID = c.ID,
ColorName = c.Name
})
.AsEnumerable()
.Select(x => x.ToExpando());
return View(select);
An anonymous object is not the same thing as a dynamic. If you want to use it like a dynamic then cast it to that:
#foreach (dynamic m in Model)
However, dynamics are best avoided if at all possible. You lose all compile-time checking and even intellisense. You won't know if you fat-fingered a property name until runtime or even if you've accidentally used the wrong type of thing the wrong way until runtime. If something is broken, you want to know about it at compile-time, not when it's already live and affecting users, when you may not even know that an error has occurred unless a user notifies you. That's a horrible situation for your app to be in.
Long and short, use strong types. If you want something with properties, ColorID and ColorName, create a view model with those properties and select your query into instances of that type. Then, everything will be nice and strongly-typed and you'll know well in advance if there's some error or problem with your code.
I have categories table, see its design:
And I want to display this nested categories inside dropdown list like this image:
Can anybody help me to find a solution?
You should retrieve all your categories, ordered by ParentId and then by Name if you need that. You should do this either in your controller or better trough a Service layer that talks to your repository or EntityFramework Datacontext. You didn't specify your data access strategy.
Then in your controller, you'll have to iterate over all categories and create each item taking into account the parent relationship. For example, if you iterate over "top categories" you could then add all child categories of the current category. If you have more than 1 level nesting, you would have to use recursion.
Pseudo-code written directly here, probably won't compile as-is:
//Assume you want to produce a SelectList to use in your View, let's use a view model like the following
public class CategoryViewModelItem
{
public string Value {get;set;}
public string Text {get;set;}
}
In your controller/service layer:
List<CategoryViewModelItem> items = new List<CategoryViewModelItem>();
//get all of them from DB
List<Category> allCategories = dataContext.Categories.ToList();
//get parent categories
List<Category> parentCategories = allCategories.Where(c => c.ParentId == null)
.OrderBy(c => c.Title);
foreach(var cat in parentCategories)
{
//add the parent category to the item list
items.Add(new CategoryViewModelItem { Value = cat.Id, Text = cat.Title });
//now get all its children (separate function in case you need recursion)
GetSubTree(allCategories, cat, items);
}
private void GetSubTree(IList<Category> allCats, Category parent, IList<CategoryViewModelItem> items)
{
var subCats = allCats.Where(c => c.ParentId == parentId);
foreach(var cat in subCats)
{
//add this category
items.Add(new CategoryViewModelItem { Value = cat.Id, Text = parent.Title + " >> " + cat.Title });
//recursive call in case your have a hierarchy more than 1 level deep
GetSubTree(allCats, cat, items);
}
}
Then to render your SelectList, you could send the SelectList as your model (or part of it) for your view:
//in your controller's action
SelectList select = new SelectList(items, "Value", "Text");
return View(select);
And use that in your View:
#Html.DropDownList("Categories", #Model)
I'm trying to follow the MVC Music Store tutorial , but I got an error which I can't handle. I've created the action:
public ActionResult Browse(string category)
{
using (OnlineStoreDbContext db = new OnlineStoreDbContext())
{
// Get category and its associated products
var categoryModel = db.Categories.Include("Products")
.Single(c => c.Title == category);
return View(categoryModel);
}
}
Than I created and the respective View:
#model OnlineStoreMVC.Core.Models.Category
#{
ViewBag.Title = "Browse";
}
<h2>Browse Category: #Model.Title</h2>
<ul>
#foreach (var product in Model.Products)
{
<li>
#product.Title
</li>
}
</ul>
But when I try to open: http://localhost:51642/Store/Browse?cat=Action, I get error:
"Sequence contains no elements" regarding this line:
var categoryModel = db.Categories.Include("Products")
.Single(c => c.Title == category);
I've alredy tried to replace Single with SingleOrDefault, but then the error was
"Object reference not set to an instance of an object." regarding that line in the View: "<h2>Browse Category: #Model.Title</h2>"
The problem is that you're passing cat as key in you're url and it should be category. So you should call http://localhost:51642/Store/Browse?category=Action
About the the error "Object reference not set to an instance of the object" you have to change you Action method to:
public ActionResult Browse(string category)
{
using (OnlineStoreDbContext db = new OnlineStoreDbContext())
{
// Get category and its associated products
var categoryModel = db.Categories.Include("Products")
.SingleOrDefault(c => c.Title == category);
if (categoryModel == default(Category))
{
categoryModel = new Category();
categoryModel.Products = new List<Product>();
}
return View(categoryModel);
}
}